package concurrent.c_010_ThreadLocal;

import util.ThreadUtil;

import java.text.DateFormat;
import java.text.SimpleDateFormat;

/**
 * https://www.cnblogs.com/jhxxb/p/14489301.html
 * https://www.bilibili.com/video/BV1N741127FH
 * <pre>
 *     早期实现
 *     ThreadLocal -> ThreadLocal#ThreadLocalMap(ThreadLocal持有) -> key:Thread,value:与 Thread 一一对应的值
 *     现在实现
 *     Thread -> ThreadLocal#ThreadLocalMap(Thread持有) -> key:ThreadLocal,value:与 Thread 一一对应的值
 *     ThreadLocal 结构
 *     public class ThreadLocal<T> {
 *         static class ThreadLocalMap {
 *             static class Entry extends WeakReference<ThreadLocal<?>> {
 *     key 是弱引用(下一次 GC 时会被回收)，将 ThreadLocal 生命周期和 Thread 生命周期解绑
 * </pre>
 * https://blog.csdn.net/f641385712/article/details/104573489
 * <p>
 * ThreadLocal 是线程本地的变量，只要是本线程内都可以使用，线程结束了，那么相应的线程本地变量也就跟随着线程消失了。
 * <p>
 * ThreadLocal 是使用空间换时间，synchronized 是使用时间换空间
 * 比如在 hibernate 中 session 就存在与 ThreadLocal中，避免 synchronized 的使用
 * <p>
 * ThreadLocal 和 InheritableThreadLocal 均是 JDK1.2 新增的 API，在 JDK5 后支持到了泛型。
 * 它表示线程局部变量：为当前线程绑定一个变量，这样在线程的声明周期内的任何地方均可取出。
 * <p>
 * InheritableThreadLocal 继承自 ThreadLocal，在其基础上扩展了能力：不仅可在本线程内获取绑定的变量，在子线程内亦可获取到。
 * 请注意：必须是子线程，若你是线程池就可能不行，因为线程池里的线程是实现初始化好的，并不一定是你的子线程
 * <p>
 * 虽然每个绑定关系都是使用的 WeakReference，但是还是建议显示的做好 remove() 移除动作，否则容易造成内存泄漏
 * <p>
 * ThreadLocal 不能解决共享变量的线程安全问题，不是拷贝了一个副本，是因为这里面所谓的变量副本都是引用传递
 * ThreadLocal 并不是为了解决线程安全问题，而是提供了一种将变量绑定到当前线程的机制，类似于隔离的效果。
 * ThreadLocal 跟线程安全基本不搭边：线程安全 or 不安全取决于绑上去的实例是怎样的：
 * * 每个线程独享一份 new 出来的实例 -> 线程安全
 * * 多个线程共享一份引用类型实例 -> 线程不安全
 */
public class T01_ThreadLocal {
    static final ThreadLocal<Person> tl1 = new ThreadLocal<>();
    static final ThreadLocal<Person> tl2 = new InheritableThreadLocal<>();
    public static final ThreadLocal<DateFormat> DATE_FORMAT_THREAD_LOCAL = new InheritableThreadLocal<>() {
        @Override
        protected DateFormat initialValue() { // 线程安全，每个线程使用的对象都是 new 出来的
            return new SimpleDateFormat("yyyy-MM-dd");
        }
    };

    public static void main(String[] args) {
        Person p = new Person();
        Person person = new Person();

        tl1.set(person);
        // System.out.println("main-tl1: " + tl1.get());
        // tl1.set(p); // 可以修改
        // System.out.println("main-tl1: " + tl1.get());
        tl2.set(person);

        new Thread(() -> {
            tl1.set(p);
            tl2.set(p);
            System.out.println("thread1-set-tl1-tl2: " + p);
            ThreadUtil.sleepSeconds(1);
        }).start();

        new Thread(() -> {
            ThreadUtil.sleepSeconds(2);
            System.out.println("thread2-tl1: " + tl1.get());
            System.out.println("thread2-tl2: " + tl2.get());
            System.out.println("thread2-DATE_FORMAT_THREAD_LOCAL: " + DATE_FORMAT_THREAD_LOCAL.get());
        }).start();

        System.out.println("main-tl1: " + tl1.get());
        System.out.println("main-tl2: " + tl2.get());
        System.out.println("main-DATE_FORMAT_THREAD_LOCAL: " + DATE_FORMAT_THREAD_LOCAL.get());
    }

    static class Person {
        String name = "zhangsan";
    }
}